package main

import (
	"errors"
	"fmt"
	"strings"
)

const rows, columns = 9, 9

// Grid is a Sudoku grid
type Grid [rows][columns]int8

type Sudoku struct {
	Grid
	copyGrid [rows][columns]int8
}

// Errors that could occur.
var (
	ErrBounds = errors.New("out of bounds")
	ErrValid  = errors.New("invalid location")
	ErrDigit  = errors.New("invalid digit")
	ErrRow    = errors.New("行重复")
	ErrColumn = errors.New("列重复")
	ErrClub   = errors.New("子网格重复")
)

// SudokuError is a slice of errors.
type SudokuError []error

// Error returns one or more errors separated by commas.
func (se SudokuError) Error() string {
	var s []string
	for _, err := range se {
		s = append(s, err.Error())
	}
	return strings.Join(s, ", ")
}

// Set a digit on a Sudoku grid
func (sdk *Sudoku) Set(row, column int, digit int8) error {
	var errs SudokuError
	if !inBounds(row, column) {
		errs = append(errs, ErrBounds)
	}
	// 修改了初始非0值
	if !sdk.setValid(row, column) {
		errs = append(errs, ErrValid)
	}
	// 行重复
	if !sdk.rowRepeate(row, digit) {
		errs = append(errs, ErrRow)
	}
	// 列重复
	if !sdk.columnRepeate(column, digit) {
		errs = append(errs, ErrColumn)
	}
	// 子网格重复
	if !sdk.clubRepeate(row, column, digit) {
		errs = append(errs, ErrClub)
	}

	if !validDigit(digit) {
		errs = append(errs, ErrDigit)
	}
	if len(errs) > 0 {
		return errs
	}

	sdk.Grid[row][column] = digit
	return nil
}

func (sdk *Sudoku) Clear(row, column int) error {
	var errs SudokuError
	if !inBounds(row, column) {
		errs = append(errs, ErrBounds)
	}
	// 修改了初始非0值
	if !sdk.setValid(row, column) {
		errs = append(errs, ErrValid)
	}

	if len(errs) > 0 {
		return errs
	}

	sdk.Grid[row][column] = 0
	return nil
}

func inBounds(row, column int) bool {
	if row < 0 || row >= rows {
		return false
	}
	if column < 0 || column >= columns {
		return false
	}
	return true
}

func (sdk Sudoku) setValid(row, column int) bool {
	return sdk.copyGrid[row][column] == 0
}
func (sdk Sudoku) rowRepeate(row int, digit int8) bool {
	for i := 0; i < columns; i++ {
		if sdk.Grid[row][i] == digit {
			return false
		}
	}
	return true
}
func (sdk Sudoku) columnRepeate(column int, digit int8) bool {
	for i := 0; i < rows; i++ {
		if sdk.Grid[i][column] == digit {
			return false
		}
	}
	return true
}
func (sdk Sudoku) clubRepeate(row, column int, digit int8) bool {
	rowClub := row / 3
	columnClub := column / 3

	for i, iMax := rowClub*3, rowClub*3+3; i < iMax; i++ {
		for j, jMax := columnClub*3, columnClub*3+3; j < jMax; j++ {
			if digit == sdk.Grid[i][j] {
				return false
			}
		}
	}
	return true
}

func validDigit(digit int8) bool {
	return digit >= 1 && digit <= 9
}

func NewSudoku(sudokuGrid [rows][columns]int8) Sudoku {
	return Sudoku{sudokuGrid, sudokuGrid}
}

func main() {
	sdk := NewSudoku([rows][columns]int8{
		{5, 3, 0, 0, 7, 0, 0, 0, 0},
		{6, 0, 0, 1, 9, 5, 0, 0, 0},
		{0, 9, 8, 0, 0, 0, 0, 6, 0},
		{8, 0, 0, 0, 6, 0, 0, 0, 3},
		{4, 0, 0, 8, 0, 3, 0, 0, 1},
		{7, 0, 0, 0, 2, 0, 0, 0, 6},
		{0, 6, 0, 0, 0, 0, 2, 8, 0},
		{0, 0, 0, 4, 1, 9, 0, 0, 5},
		{0, 0, 0, 0, 8, 0, 0, 7, 9},
	})
	// err := sdk.Clear(0, 0)
	err := sdk.Set(0, 2, 6)
	if err != nil {
		if errs, ok := err.(SudokuError); ok {
			fmt.Printf("%d error(s) occurred:\n", len(errs))
			for _, e := range errs {
				fmt.Printf("- %v\n", e)
			}
		}
	}
	for i := 0; i < rows; i++ {
		fmt.Println(sdk.Grid[i])
	}
}
